package yxy.cherry.chat;

import java.util.ArrayList;
import java.util.Collection;
import java.util.ServiceLoader;

import com.alibaba.fastjson.parser.ParserConfig;

import yxy.apple.database.DAOAccountManager;
import yxy.apple.database.DAOGameManager;
import yxy.apple.database.DAOManager;
import yxy.apple.logger.Logger;
import yxy.apple.util.RuntimeUtility;
import yxy.apple.util.RuntimeUtility.ShutdownHookable;
import yxy.apple.util.redisson.FastjsonCodec;
import yxy.apple.util.redisson.RedisConnection;
import yxy.banana.launcher.JSONBeanConfiguration;
import yxy.cherry.chat.message.ConsumerImpl;
import yxy.cherry.chat.message.MessageDispatcher;
import yxy.cherry.data.DataCenter;
import yxy.cherry.data.actdata.DataCenterAct;
import yxy.cherry.data.dfa.Sensitive;
import yxy.cherry.data.script.FomulaScript;
import yxy.cherry.game.base.bean.ChatBean;
import yxy.cherry.game.base.bean.GMInnerBean;
import yxy.cherry.game.base.module.IGameModule;
import yxy.cherry.game.base.mq.MqManager;
import yxy.cherry.game.base.redis.RedisManager;
import yxy.cherry.game.base.redis.RedisType;
import yxy.game.pm2.bean.area.AreaBean;

public class Chat {
	public static String ApplicationName = "Chat";
	public static String ReleaseVersion = "1.0-SNAPSHOT";
	public static String ReleaseDate = "20200801";
	public static String Slogan = " -= " + ApplicationName + " V" + ReleaseVersion + " Release " + ReleaseDate + " =-";

	protected static Logger logger = Logger.create(Chat.class);

	private static class Cleaner implements ShutdownHookable {
		@Override
		public void shutting() {
			logger.info("正在关闭%s...", ApplicationName);
			RedisManager.getInstance().disconnect();
			Reactor.instance.shutdown();
			logger.info("完成%s全部关闭工作，Bye!", ApplicationName);
		}
	}

	public void start() {
		ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
		try {
			logger.info(Slogan);
			logger.info("正在启动...");
			logger.info("正在载入配置...");
			Configuration config = initConfig();

			logger.info("设置日志等级为%s", config.LogLevel.name());
			Logger.setLevel(config.LogLevel);
			logger.info("加载逻辑服消息处理集...");
			initCommand();
			logger.info("正在添加ShutdownHook服务...");
			RuntimeUtility.addShutdownHook(new Cleaner());

			logger.info("正在初始化数据库连接...");
			DAOManager.init(config.DBAccountConfig);
			logger.info("正在初始化数据库连接2...");

			logger.info("正在初始化数据库连接...");
			DAOGameManager.init(config.DBGameConfig);
			logger.info("正在初始化数据库连接3...");

			DAOAccountManager.init(config.DBAccountConfig);

			logger.info("正在初始化Redis连接...");
			initRedis(config);
			logger.info("正在初始化MQ连接...");
			initMQ(config);

			Reactor.instance.start(config);

			logger.info("添加消息消费者...");
			initConsumer(config);

			logger.info("%s服务器启动完成!", ApplicationName);

			Reactor.instance.http(config);
		} catch (Exception e) {
			logger.error(e);
			System.exit(5);
		}
	}

	private Configuration initConfig() throws Exception {
		Configuration config = null;
		String outsideConfig = null;
// XXX 屏蔽Lagos	
//		try {
//			ConfigChangeManager configChangeManager = new ConfigChangeManager();
//			configChangeManager.addSubcribe();
//			outsideConfig = Lagos.getInstance().load();
//		} catch (Exception e) {
//			logger.error("读取外部配置文件失败", e);
//			outsideConfig = null;
//		}
		if (outsideConfig == null) {
			String filename = System.getProperty("user.dir") + "/config.json";
			logger.info("加载配置文件%s...", filename);
			config = JSONBeanConfiguration.load(filename, Configuration.class);
//XXX 屏蔽Lagos				
//		} else {
//			logger.info("加载nacos配置\n%s", outsideConfig);
//			config = JSONBeanConfiguration.load(JSONObject.parseObject(outsideConfig), Config.class);
		}

		JSONBeanConfiguration.share(Configuration.SharedKey, config);
		return config;
	}

	private void initMQ(Configuration config) throws Exception {
		MqManager.getInstance().init(config.MqConfig);
	}

	private void initRedis(Configuration config) throws Exception {
		{
			RedisConnection redisUser = new RedisConnection();
			redisUser.connect(config.UserRedis.Address, config.UserRedis.Password, config.UserRedis.Database, true, new FastjsonCodec());
			RedisManager.getInstance().addConnection(RedisType.REDIS_USER, redisUser);

			ChatBean.sign(redisUser, config.SystemID, config.ClusterID);
			GMInnerBean.sign(redisUser, config.HTTPServer.LocalIP, config.HTTPServer.Port,
					config.HTTPServer.SSL == null ? false : config.HTTPServer.SSL.Enabled);
		}
		{
			RedisConnection redisGame = new RedisConnection();
			redisGame.connect(config.GameRedis.Address, config.GameRedis.Password, config.GameRedis.Database, true, new FastjsonCodec());
			RedisManager.getInstance().addConnection(RedisType.REDIS_GAME, redisGame);
		}
		{
			RedisConnection redisData = new RedisConnection();
			redisData.connect(config.DataRedis.Address, config.DataRedis.Password, config.DataRedis.Database, true, new FastjsonCodec());
			RedisManager.getInstance().addConnection(RedisType.REDIS_DATA, redisData);

			DataCenter.init(redisData,false);
			FomulaScript.init(redisData);
			Sensitive.init(redisData);

			initDataCenterAct();
		}

	}

	private void initCommand() throws Exception {
		// 根据游戏包提供的resurces/META-INF/services/yxy.cherry.game.base.module.IGameModule文件内容获取IGameModule子类
		// 每个游戏包都需要提供响应的IGameModule实现来定位其Message和Command包地址
		ServiceLoader<IGameModule> serviceLoaders = ServiceLoader.load(IGameModule.class);
		if (!serviceLoaders.iterator().hasNext()) {
			logger.error("游戏包加载失败，请检查pom配置");
			throw new Exception("IGameModule is empty");
		}
		for (IGameModule gameModule : serviceLoaders) {
			gameModule.init();
			MessageDispatcher.getInstance().initMessage(gameModule);
		}
	}

	private void initConsumer(Configuration config) {
		// 配置消费者线程，需要根据config里配置的数量来定义消费者数量
		for (int i = config.MessageStationThreads; i > 0; i--) {
			Reactor.instance.station().treat(new ConsumerImpl());
		}
	}

	public static void main(String[] args) {
		new Chat().start();
	}

	private void initDataCenterAct() {
		Collection<AreaBean> abs = AreaBean.all();
		ArrayList<Integer> areaids = new ArrayList<Integer>();
		for (AreaBean ab : abs) {
			areaids.add(ab.getAreaId());
		}

		DataCenterAct.init(RedisManager.getInstance().getConnection(RedisType.REDIS_DATA), false, areaids);
	}
}
